# ！/usr/bin/ python3
# -*- codeing = utf-8 -*-
# @Time :2022/1/2 15:53
# @Author:wuhha
# @File : tasks.py
# @Software: PyCharm
import datetime
import os
import logging  # 后台任务需要增加日志记录，否则出现问题后难以排查
import jieba
from celery.schedules import crontab
from celery.task import periodic_task
from PIL import Image
import numpy
from wordcloud import WordCloud
import matplotlib
matplotlib.use('Agg')#在不需要展示图片时，使用matplotlib 的非交互模式，如 agg；
from matplotlib import pyplot as plt
from io import BytesIO
import base64

from django.contrib.sessions.models import Session

from config.default import BASE_DIR

from youpu.models import VerifyCode, User, Question, HotList, Article, Wordcloud

logger = logging.getLogger("root")


# 定时任务：每周一清晨一点清除无效验证码，减少数据库非必要冗余
@periodic_task(run_every=crontab(minute=0, hour=1, day_of_week='monday'))
def clear_invalid_verifyCode():
    logger.info("clear_invalid_verifyCode开始执行")
    valid_end = (datetime.datetime.now() + datetime.timedelta(minutes=-5))  # 验证码有效时间只有5分钟
    invalid_verifyCodes = VerifyCode.objects.filter(updated_at__lt=valid_end)
    invalid_verifyCodes.delete()
    logger.info("clear_invalid_verifyCode执行完毕")


# 定时任务：每月的第一天清晨两点清除无效session数据，减少数据库非必要冗余【代替django-admin clearsessions命令起作用】
@periodic_task(run_every=crontab(minute=0, hour=2, day_of_month=1))
def clear_invalid_sessionData():
    logger.info("clear_invalid_sessionData开始执行")
    invalid_sessions = Session.objects.filter(expire_date__lt=datetime.datetime.now())  # 筛选出过期时间已经到达的session
    invalid_sessions.delete()
    logger.info("clear_invalid_sessionData执行完毕")


# 定时任务：每晚12点计算、更新各用户的一周油力值
@periodic_task(run_every=crontab(minute=0, hour=0))
def update_users_oneweek_energy():
    logger.info("update_users_oneweek_energy开始执行")
    users = User.objects.all()
    for user in users:
        # 删除最早一条的数据，并将新一天的数据加到列表的最后
        new_oneweek_energy = user.get_oneweek_energy()
        del new_oneweek_energy[0]
        new_oneweek_energy.append(user.today_energy)
        user.set_oneweek_energy(new_oneweek_energy)
        # 将昨天计算出的油力值清0
        user.today_energy = 0
        user.save()#操作完记得保存！
    logger.info("update_users_oneweek_energy执行完毕")


# 定时任务：每隔3分钟计算一次问答热榜
@periodic_task(run_every=crontab(minute='*/3'))
def update_hotlist_question():
    logger.info("update_hotlist_question开始执行")
    # 0.数据库保留一个空问题，用于热榜补位
    try:
        placeholder_question = Question.objects.get(title="暂无数据，等待占位")
    except Exception:
        placeholder_question = Question.objects.create(title="暂无数据，等待占位")
    # 1.获取所有问题
    question_hotlist = [question for question in Question.objects.all()]
    # 2.将问题按热度值降序排列【热度值：回答数 + 问题点赞数】，生成一个10位的列表
    question_hotlist.sort(key=lambda x: x.get_hotscore(), reverse=True)
    if len(question_hotlist) < 10:
        while len(question_hotlist) < 10:  # 将question_hotlist列表填充到10
            question_hotlist.append(placeholder_question)
    elif len(question_hotlist) > 10:  # 多于10个，则仅保留10个
        question_hotlist = question_hotlist[:10]
    # 3.将新产生的热度榜的数据插入到数据库
    for index, hot_question in enumerate(question_hotlist):  # 先删除旧榜单数据，再添加新榜单数据
        try:  # 有旧记录则删除，防止数据库数据过多
            old_hot_question = HotList.objects.get(type='Q', rank=index + 1)
            old_hot_question.delete()
        except:
            old_hot_questions = HotList.objects.filter(type='Q', rank=index + 1) #解决bug：由于线上环境不稳定，可能会出现任务断流的情况，导致bug：热榜数据不断增加
            for old_hot_question in old_hot_questions:
                old_hot_question.delete()
        HotList.objects.create(type='Q', rank=index + 1, hot_object=hot_question)
    logger.info("update_hotlist_question执行完毕")


# 定时任务：每隔5分钟计算一次文章热榜
@periodic_task(run_every=crontab(minute='*/5'))
def update_hotlist_article():
    logger.info("update_hotlist_article开始执行")
    # 0.数据库保留一个空文章，用于热榜补位
    try:
        placeholder_article = Article.objects.get(title="暂无数据，等待占位")
    except Exception:
        placeholder_article = Article.objects.create(title="暂无数据，等待占位")
    # 1.获取所有文章
    article_hotlist = [article for article in Article.objects.all()]
    # 2.将文章按热度值降序排列【热度值：文章点赞数】，生成一个10位的列表
    article_hotlist.sort(key=lambda x: x.count_likers(), reverse=True)
    if len(article_hotlist) < 10:
        while len(article_hotlist) < 10:  # 将question_hotlist列表填充到10
            article_hotlist.append(placeholder_article)
    elif len(article_hotlist) > 10:  # 多于10个，则仅保留10个
        article_hotlist = article_hotlist[:10]
    # 3.将新产生的热度榜的数据插入到数据库
    for index, hot_article in enumerate(article_hotlist):  # 先删除旧榜单数据，再添加新榜单数据
        try:  # 有旧记录则删除，防止数据库数据过多
            old_hot_article = HotList.objects.get(type='A', rank=index + 1)
            old_hot_article.delete()
        except:
            pass
        HotList.objects.create(type='A', rank=index + 1, hot_object=hot_article)
    logger.info("update_hotlist_article执行完毕")


# 定时任务：每隔6小时更新一次词云数据
# @periodic_task(run_every=crontab(hour='*/6'))#此任务会导致进程被杀掉，暂时注释
def update_wordcloud_data():
    logger.info("update_wordcloud_data开始执行")
    # 1、将所有热榜的数据得到，将标题、内容拼起来形成一个大字符串
    text = ""
    hotlist_article = HotList.objects.filter(type="A")
    for hot_article in hotlist_article:
        if (not hot_article.hot_object.title == "暂无数据，等待占位"):  # 若非占位数据，则计入统计范畴
            text = text + hot_article.hot_object.title + hot_article.hot_object.title + hot_article.hot_object.title + hot_article.hot_object.content  # 标题权重为3，内容为1
    hotlist_question = HotList.objects.filter(type="Q")
    for hot_question in hotlist_question:
        if (not hot_question.hot_object.title == "暂无数据，等待占位"):  # 若非占位数据，则计入统计范畴
            text = text + hot_question.hot_object.title + hot_question.hot_object.title + hot_question.hot_object.title + hot_question.hot_object.content  # 标题权重为3，内容为1
    # 2、分词
    wordsList = jieba.cut(text)
    words = " ".join(wordsList)
    # 3、生成词云
    img = Image.open(os.path.join(BASE_DIR, "static/img/cloud.jpg"))
    img_array = numpy.array(img)
    wc = WordCloud(
        background_color='white',  # 输出图片的颜色的设置
        mask=img_array,
        font_path=os.path.join(BASE_DIR, "static/fonts/for_wordcloud.ttf"),
    )
    wc.generate_from_text(words)
    # 4、生成图片数据
    # fig = plt.figure(1, dpi=1200)
    fig = plt.figure(1, dpi=1000)#1200服务器会kill掉我的进程，试试1000
    plt.imshow(wc)
    plt.axis('off')  # 不显示坐标轴
    # 5、保存二进制数据到数据库
    sio = BytesIO()
    fig.savefig(sio, format='png', bbox_inches='tight', pad_inches=0.0)
    data = base64.encodebytes(sio.getvalue()).decode()
    src = 'data:image/png;base64,' + str(data)
    for olddata in Wordcloud.objects.all():  # 删除旧数据
        olddata.delete()
    Wordcloud.objects.create(wordcloud_data=src)
    plt.close()#图像保存后及时清理内存
    logger.info("update_wordcloud_data执行完毕")
